package com.ticket.sass.admin.business.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ticket.sass.admin.business.CouponCodeBusiness;
import com.ticket.sass.admin.dto.input.coupons.code.CouponCodeBatchListInput;
import com.ticket.sass.admin.dto.input.coupons.code.CouponCodeListInput;
import com.ticket.sass.admin.dto.input.coupons.code.CouponGenerateInput;
import com.ticket.sass.admin.dto.output.coupons.code.CouponCodeBatchListOutput;
import com.ticket.sass.admin.dto.output.coupons.code.CouponCodeListOutput;
import com.ticket.sass.admin.model.PageModel;
import com.ticket.sass.admin.service.impl.CouponCodeBatchServiceImpl;
import com.ticket.sass.admin.service.impl.CouponCodeServiceImpl;
import com.ticket.sass.admin.service.impl.CouponServiceImpl;
import com.ticket.sass.admin.service.impl.CouponUserMapServiceImpl;
import com.ticket.sass.admin.util.CinemaIdUtil;
import com.ticket.sass.admin.util.ModelUtil;
import com.ticket.sass.admin.util.RedisCacheUtil;
import com.ticket.sass.common.entity.tenant.CouponCodeBatchEntity;
import com.ticket.sass.common.entity.tenant.CouponCodeEntity;
import com.ticket.sass.common.entity.tenant.enums.CouponUserMapUseState;
import lombok.AllArgsConstructor;
import org.modelmapper.TypeToken;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author ywb
 * @date 2024-07-04 11:24
 */
@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class CouponCodeBusinessImpl implements CouponCodeBusiness {
    
    final ModelUtil modelUtil;
    final CouponServiceImpl service;
    final RedisCacheUtil redisCacheUtil;
    final CouponCodeServiceImpl couponCodesService;
    final CouponUserMapServiceImpl couponUserMapService;
    final CouponCodeBatchServiceImpl couponCodeBatchesService;
    
    @Override
    public PageModel<CouponCodeListOutput> list(Long couponId, CouponCodeListInput input) {
        List<String> codes = couponUserMapService.codeBy(couponId, (short) 1);

        IPage<CouponCodeEntity> page = couponCodesService.iPage(couponId, input, codes);
        List<CouponCodeListOutput> areas = modelUtil.listToList(
            page.getRecords(),
            new TypeToken<List<CouponCodeListOutput>>() {}.getType());
        List<String> codeBys = areas.stream().map(CouponCodeListOutput::getCode).collect(Collectors.toList());
        
        Map<String, CouponUserMapUseState> couponUseStateByCouponId1 = couponUserMapService.getCouponUseStateByCouponId(couponId, codeBys);
        
        
        areas.forEach(x -> x.setUseState(couponUseStateByCouponId1.getOrDefault(x.getCode(), CouponUserMapUseState.UNUSED)));
        
        return new PageModel<>(page.getTotal(), areas);
    }
    
    @Override
    public PageModel<CouponCodeBatchListOutput> batchList(Long couponId, CouponCodeBatchListInput input) {
        IPage<CouponCodeBatchEntity> page = couponCodeBatchesService.iPage(couponId, input);
        List<CouponCodeBatchListOutput> outputList = modelUtil.listToList(
            page.getRecords(),
            new TypeToken<List<CouponCodeBatchListOutput>>() {}.getType());
        
        // 获取每个批次的兑换码数量
        List<String> batchNums = outputList.stream().map(CouponCodeBatchListOutput::getBatchNum).collect(Collectors.toList());
        Map<String, Integer> codeCountMap = couponCodesService.getCouponCodeCountByBatchNumsAndCouponId(batchNums, couponId);
        
        // 将兑换码数量绑定到输出结果中
        for (CouponCodeBatchListOutput output : outputList) {
            output.setCodeCount(codeCountMap.getOrDefault(output.getBatchNum(), 0));
        }
        
        return new PageModel<>(page.getTotal(), outputList);
    }
    @Override
    public void generate(Long id, CouponGenerateInput input) {
        Long cinemaIdFromRequest = CinemaIdUtil.getCinemaIdFromRequest();
        
        // 生成批次号
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
        String batchNum = LocalDateTime.now().format(formatter);
        
        // 创建批次表记录
        CouponCodeBatchEntity batchEntity = new CouponCodeBatchEntity();
        batchEntity.setCouponId(id);
        batchEntity.setBatchNum(batchNum);
        batchEntity.setName(input.getBatchName());
        batchEntity.setCinemaId(cinemaIdFromRequest);
        couponCodeBatchesService.save(batchEntity);
        
        int totalNumber = input.getNumber();
        int batchSize = Math.min(totalNumber, 500); // 每批不超过500个兑换码
        List<CouponCodeEntity> couponCodeEntities = new ArrayList<>();
        Set<String> uniqueCodes = new HashSet<>();
        
        while (totalNumber > 0) {
            List<CouponCodeEntity> batchCouponCodes = new ArrayList<>();
            for (int i = 0; i < batchSize; i++) {
                String code = generateRandomCode(16);
                // 确保生成的兑换码是唯一的
                while (!uniqueCodes.add(code)) {
                    code = generateRandomCode(16);
                }
                CouponCodeEntity couponCodeEntity = new CouponCodeEntity();
                couponCodeEntity.setBatchNum(batchNum);
                couponCodeEntity.setCouponId(id);
                couponCodeEntity.setCode(code);
                couponCodeEntity.setTake(Boolean.FALSE);
                couponCodeEntity.setCinemaId(cinemaIdFromRequest);
                batchCouponCodes.add(couponCodeEntity);
            }
            couponCodeEntities.addAll(batchCouponCodes);
            totalNumber -= batchSize;
        }
        
        // 检查并移除重复的兑换码
        List<String> duplicates = checkDuplicateCodes(couponCodeEntities);
        if (!duplicates.isEmpty()) {
            // 从列表中移除重复的兑换码
            couponCodeEntities.removeIf(entity -> duplicates.contains(entity.getCode()));
            // 补足缺少的兑换码数量
            while (couponCodeEntities.size() < input.getNumber()) {
                String newCode = generateRandomCode(16);
                if (uniqueCodes.add(newCode)) {
                    CouponCodeEntity newEntity = new CouponCodeEntity();
                    newEntity.setBatchNum(batchNum);
                    newEntity.setCouponId(id);
                    newEntity.setCode(newCode);
                    newEntity.setCinemaId(cinemaIdFromRequest);
                    newEntity.setTake(Boolean.FALSE);
                    couponCodeEntities.add(newEntity);
                }
            }
        }
        
        // 保存兑换码
        couponCodesService.saveBatch(couponCodeEntities);
    }
    
    // 检查兑换码是否重复，并返回重复的兑换码列表
    public List<String> checkDuplicateCodes(List<CouponCodeEntity> couponCodes) {
        return couponCodesService.checkDuplicateCodes(couponCodes);
    }
    
    @Override
    public String exportToken() {
        String hashString = Integer.toHexString(UUID.randomUUID().toString().hashCode());
        redisCacheUtil.setCacheObject(hashString, 1);
        redisCacheUtil.setKeyExpireTime(hashString, 5, TimeUnit.MINUTES);
        return hashString;
    }
    
    @Override
    public Boolean existExportToken(String token) {
        // 检查Redis中是否存在该token
        return redisCacheUtil.getCacheObject(token) != null;
    }
    @Override
    public void delExportToken(String token) {
        // 检查并删除令牌，确保它不再可用
        redisCacheUtil.deleteObject(token);
    }
    
    private String generateRandomCode(int length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10)); // 生成 0-9 的随机数字
        }
        return sb.toString();
    }
}
